/*
 * ===== T H E   K N O B =====
 *   Media Controller Mk III
 * ----- By Frank Lumien -----
 *        www.lumien.se
 * ===========================
 *       Arduino code by:
 *  IAmOrion aka James Tanner
 * ===========================
 * 
 * Version 2.1
 * 
 * FUNCTIONS
 * Turn knob left: Volume down
 * Turn knob right: Volume up
 * Press knob: Play & Pause (alternating)
 * Press and turn knob left: Previous track
 * Press and turn knob right: Next track
 * Note: You can change all of these functions, see Settings.h for more.
  *  
 * PINOUT, Digispark to KY-040
 * P0   to  CLK
 * P1   to  DT
 * P2   to  SW
 * VIN  to  GND
 * GND  to  +5V
 * Note: The position of these pins may depend on your board and encoder.
 * If you have already soldered or if The Knob doesn’t behave as expected,
 * you can adjust the pin settings in Settings.h
 * 
 * CONNECTING
 * 安装驱动
 * See https://digistump.com/wiki/digispark/tutorials/connecting for how
 * to connect, get drivers and add set up Arduino for the Digispark bord.
 * 
 * CONNECTING
 * 配置Arduino IDE
 * 1.在 文件->首选项 中添加开发板管理器网址：
 * https://raw.githubusercontent.com/ArminJo/DigistumpArduino/master/package_digistump_index.json
 * http://digistump.com/package_digistump_index.json
 * 2.在 工具->开发板->开发板管理器 中搜索 “Digistump AVR Boards” 并安装
 * 3.开发板选择 Digispark
 * Set board to "Digispark (Default - 16.5Mhz)"
 * 
 * COMPILING
 * 安装所需的库
 * Get the Trinket library from https://github.com/adafruit/Adafruit-Trinket-USB/tree/master/TrinketHidCombo
 * When compiling you will likely get warning messages related to the Trinket 
 * library. This is normal and does not mean that something is wrong. As long 
 * as the compilation finishes, you are good to go.
 */

// Debug option, uncomment next line if you want to output commands as text (e.g. to Notepad).
//#define KB_DEBUG

#ifndef KB_DEBUG
  #include "TrinketHidCombo.h"
#else
  #include <TrinketKeyboard.h>
#endif

#include "Settings.h" 

#define TRINKET

#ifdef TRINKET
  #include <avr/power.h>
#endif

#define LATCHSTATE 3
int buttonState = LOW, lastButtonState = LOW;
long lastDebounceTime = 0, debounceDelay = 50;
int _position = 0, _positionExt = 0;
int8_t _oldState; bool btnPressed=false, btnReleased=false, pressedRotary=false;

const int8_t encoderStates[] = {
  0, -1, 1, 0,
  1, 0, 0, -1,
  -1, 0, 0, 1,
  0, 1, -1, 0
};

void setup() {
  #ifdef TRINKET
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  
  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);
  pinMode(encoderButton, INPUT);
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(encoderButton, LOW);
  _oldState = 3; 
  #ifndef KB_DEBUG
    TrinketHidCombo.begin();
  #else
    TrinketKeyboard.begin();
  #endif
}

void loop() {
  static int pos = 0;
  tick();
  int newPos = getPosition();
  if (pos != newPos) {
    if (newPos < pos) {
      if (!btnPressed) {
        #ifndef KB_DEBUG
          TrinketHidCombo.pressMultimediaKey(LEFT_ACTION);
        #else
          TrinketKeyboard.println("LEFT_ACTION");
        #endif
      } else {
        pressedRotary=true;
        #ifndef KB_DEBUG
          TrinketHidCombo.pressMultimediaKey(LEFT_ACTION_MODE1);
        #else
          TrinketKeyboard.println("LEFT_ACTION_MODE1");
        #endif
      }
    }
    else if (newPos > pos){
     if (!btnPressed) {
        #ifndef KB_DEBUG
          TrinketHidCombo.pressMultimediaKey(RIGHT_ACTION);
        #else
          TrinketKeyboard.println("RIGHT_ACTION");
        #endif
      } else {
        pressedRotary=true;
        #ifndef KB_DEBUG
          TrinketHidCombo.pressMultimediaKey(RIGHT_ACTION_MODE1);
        #else
          TrinketKeyboard.println("RIGHT_ACTION_MODE1");
        #endif
      }
    }
    pos = newPos;
  }
  int reading = digitalRead(encoderButton);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == HIGH) {
        btnPressed=true;
      } else if (buttonState == LOW){
        btnReleased=true;
      }
    } 
  }
  lastButtonState = reading;

  if (btnPressed == true && btnReleased ==true && pressedRotary ==false) {
    #ifndef KB_DEBUG
      TrinketHidCombo.pressMultimediaKey(BUTTON_ACTION_SINGLE);
    #else
      TrinketKeyboard.println(btnPressed);
      TrinketKeyboard.println(btnReleased);
      TrinketKeyboard.println(pressedRotary);
      TrinketKeyboard.println("BUTTON_ACTION_SINGLE");
    #endif
    btnPressed=false;btnReleased=false;
  } else if (btnPressed == true && btnReleased == true && pressedRotary == true) {
    #ifdef KB_DEBUG
      TrinketKeyboard.println("RELEASED_AFTER_HELD_ROTATION");
    #endif
    btnPressed=false;btnReleased=false;pressedRotary=false;
  }
  #ifndef KB_DEBUG
    TrinketHidCombo.poll();
  #else
    TrinketKeyboard.poll();
  #endif
}

int  getPosition() {
  return _positionExt;
}

void setPosition(int newPosition) {
  _position = ((newPosition<<2) | (_position & 0x03));
  _positionExt = newPosition;
}

void tick(void) {
  int sig1 = digitalRead(encoderPinA);
  int sig2 = digitalRead(encoderPinB);
  int8_t thisState = sig1 | (sig2 << 1);
  if (_oldState != thisState) {
    _position += encoderStates[thisState | (_oldState<<2)];
    if (thisState == LATCHSTATE)
      _positionExt = _position >> 2;
    _oldState = thisState;
  }
} 
